异步
异步编程是处理耗时操作(如网络请求、文件读写、定时器)的核心机制。在 ES6+ 提出了 Promise、async 和 await 用于解决回调地狱(Callback hell)问题,使代码更易读和维护。
Promise: 异步操作的标准化封装
Promise 是 ES6 引入的异步操作容器,用于表示一个异步操作最终完成(或失败)以及结果值。
三种状态
pending(等待中):初始状态fulfilled(已成功):操作成功完成rejected(已失败):操作失败
状态不可逆,一旦从 pending 变成 fulfilled 或 rejected ,无法再更改
错误
Promise 的错误必须显示捕获
Promise链中的错误(reject 或抛出异常)不会终止程序,需通过.catch或try/catch(配合async/await) 捕获。
Promise.reject('错误').catch(err => {
console.log(err);
});
避免冗余使用
若异步操作本身返回 Promise,无需使用 new Promise 封装,直接返回即可。
// 冗余封装
function fetchData() {
return new Promise((resolve, reject) => {
// 直接返回 fetch 的 Promise
fetch(url).then(resolve).catch(reject);
});
}
Promise.all
Promise.all 等待所有的 Promise 成功,或任意一个失败(立即拒绝)。
当执行的数组为空,立即返回 Promise 成功。
Promise.all([fetch(url1), fetch(url2)])
.then(responses => Promise.all(responses.map(res => res.json())))
.catch(err => console.log('至少一个请求失败'));
Promise.race
Promise.race 返回第一个完成的 Promise (无论成功或是失败)。
const timeout = new Promise((_, reject) =>
setTimeout(() => reject('请求超时'), 5000),
);
Promise.race([fetch(url), timeout])
.then(res => console.log(res))
// 5秒未响应则报超时
.catch(err => console.log(err));
Promise.allSettled
ES2020 引入了 Promise.allSettled ,返回一个包含所有的 Promise 结果的对象数组,即便是不分异步操作结果是失败。
Promise.allSettled([fetch(url1), fetch(url2)]).then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});
async/await:异步代码的同步写法
async/await 是 ES2017 引入的语法糖,基于 Promise 用于简化异步代码的书写。
async
声明一个返回 Promise 的函数
async function foo() {
// 等价于 return Promise.resolve(42)
return 42;
}
await
仅可在 async 中使用,暂停函数的执行,等待右侧 Promise 解决。
async function fetchData() {
// 等待 fetch 的 Promise 完成
const res = await fetch(url);
// 等待 res.json() 的 Promise 完成
const data = await res.json();
return data;
}
注意:await 仅暂停当前 async 函数的执行,不影响其他的异步操作
async function foo() {
console.log('start');
await Promise.resolve(); // 暂停 foo,但不阻塞全局
console.log('end');
}
foo();
console.log('全局'); // 输出顺序:start → 全局 → end
循环中的 await
在循环中使用 await 会导致每一个迭代等待前一个完成:
async function processArray(array) {
for (const item of array) {
// 顺序处理(耗时累加)
await processItem(item);
}
}
执行顺序
Promise.then 是微任务,setTimeout 是宏任务
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
// 输出顺序:start → end → promise → timeout
注意
- 未处理的
Promise拒绝可能导致内存泄漏 - 避免不必要的 await:若多个异步独立,用
Promise.all并行处理 - 避免在循环中无意义的 await:合并异步操作